# -*- coding: utf-8 -*-
import os
import re
import shutil
import subprocess

from common.utils.common_functions import current_time_to_str, exit_program
from common.utils.licensee import Licensee


class AdbManager:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            # 创建新的实例
            instance = super().__new__(cls)
            cls._instance = instance
        return cls._instance

    def __init__(self):
        # 判断实例是否已经初始化
        if not hasattr(self, 'authorization_time'):
            licensee = Licensee()
            self.is_provide_demo = licensee.is_provide_demo
            self.test_start_time = current_time_to_str()
            if not os.path.exists("logs"):
                os.makedirs("logs")
            self.log_path = None
            self.system_resource_consumption_path = None
            self.update_path()
            self.app_pid = None
            self.rss_pos = -1
            self.adb = "adb"
            if not shutil.which("adb"):
                if os.path.exists("lib/platform-toolsm -/adb.exe"):
                    print("未找到 adb 环境，使用本地：lib/platform-tools/adb.exe")
                    self.adb = "lib/platform-tools/adb.exe"
                else:
                    exit_program("未找到 adb")

    # 更新日志记录路径
    def update_path(self):
        self.log_path = "logs/%s_app_monitor.log" % self.test_start_time
        print(f"日志保存路径：{self.log_path}")
        self.system_resource_consumption_path = "logs/%s_app_monitor_system_resource_consumption.txt" \
                                                % self.test_start_time
        print(f"app资源使用情况存储路径：{self.system_resource_consumption_path}")

    # 连接设备
    def connect_device(self, device_identifier):
        print(f"正在连接设备 {device_identifier}")
        try:
            adb_devices = subprocess.run([self.adb, "devices"], capture_output=True, text=True)
            output = adb_devices.stdout
            lines = output.splitlines()
            found = False
            offline = False
            for line in lines:
                if device_identifier in line:
                    found = True
                    if "offline" in line:
                        offline = True
                        break
            if found and offline:
                print(f"{device_identifier} is offline")
                print(f"{current_time_to_str()}  断开离线设备: {device_identifier}")
                subprocess.run([self.adb, "disconnect", device_identifier])
                os.system("echo %s  %s: 断开离线设备连接 >> %s" % (current_time_to_str(), device_identifier, self.log_path))
                print(f"{current_time_to_str()}  连接设备: {device_identifier}")
                subprocess.run([self.adb, "connect", device_identifier])
                os.system("echo %s  %s: 连接设备 >> %s" % (current_time_to_str(), device_identifier, self.log_path))
            elif not found:
                print(f"{current_time_to_str()}  连接设备: {device_identifier}")
                subprocess.run([self.adb, "connect", device_identifier])
                os.system("echo %s  %s: 连接设备 >> %s" % (current_time_to_str(), device_identifier, self.log_path))
        except subprocess.CalledProcessError as e:
            print(f"Error: {e}")

    # 判断设备是否连接
    def is_connect_device(self, device_identifier):
        try:
            result = subprocess.check_output([self.adb, "devices"])
            result = result.decode('utf-8')
            # 检查输出中是否包含指定的 device 且不包含 offline
            lines = result.splitlines()
            for line in lines:
                parts = line.split()
                if len(parts) >= 2 and parts[0] == device_identifier:
                    if parts[1] == "device":
                        print(f"{device_identifier} 设备已连接")
                        return True
                    elif parts[1] == "offline":
                        print(f"{device_identifier} is offline")
                        return False
            print(f"{device_identifier} 设备未连接，请确认连接状态")
            return False
        except subprocess.CalledProcessError as e:
            print(f"{device_identifier} 设备未连接，请确认连接状态")
            print(f"Error: {e}")
            return False

    # 获取图片和日志
    def capture_log_png(self, device_identifier, logcat_number):
        stop_time = current_time_to_str()
        # 使用 subprocess.run 替代 os.system
        with open(self.log_path, 'a') as f:
            try:
                if self.is_provide_demo:
                    print(f"{self.adb} -s {device_identifier} shell screencap -p /sdcard/test.png")
                    print(f"{self.adb} -s {device_identifier} shell screencap -p /sdcard/test.png")
                    subprocess.run(["echo", f"{current_time_to_str()}  获取崩溃时的图片 --> "
                                            f"logs/{self.test_start_time}_app_crash_screenshot_{stop_time}.png"],
                                   stdout=f, check=True, shell=True)
                    subprocess.run(["echo", f"{current_time_to_str()}  获取日志：\n日志内容"],
                                   stdout=f, check=True, shell=True)
                    print(f"{self.adb} -s {device_identifier} shell logcat -t {str(logcat_number)}")
                else:
                    subprocess.run([self.adb, "-s", device_identifier, "shell", "screencap", "-p", "/sdcard/test.png"], check=True)
                    subprocess.run([self.adb, "-s", device_identifier, "pull", "/sdcard/test.png",
                                    f"logs/{self.test_start_time}_app_crash_screenshot_{stop_time}.png"], check=True)
                    # 修正了 echo 命令的格式化
                    subprocess.run(["echo", f"{current_time_to_str()}  获取崩溃时的图片 --> "
                                    f"logs/{self.test_start_time}_app_crash_screenshot_{stop_time}.png"],
                                   stdout=f, check=True, shell=True)
                    subprocess.run(["echo", f"{current_time_to_str()}  获取日志："], stdout=f, check=True, shell=True)
                    subprocess.run([self.adb, "-s", device_identifier, "shell", "logcat", "-t", str(logcat_number)],
                                   stdout=f, check=True)
            except subprocess.CalledProcessError as e:
                print(f"Error: {e}")

    # 获取进程
    def get_app_pid(self, device_identifier, app_package_name):
        try:
            result = subprocess.check_output([self.adb, "-s", device_identifier, "shell", "ps"]
                                             , universal_newlines=True, shell=True)
            lines = result.splitlines()
            for line in lines:
                parts = line.split()
                if parts and parts[-1].startswith("{}:".format(app_package_name)):
                    # print("当前数据跳过：{}".format(line))
                    continue
                if app_package_name in parts:
                    # print(line)
                    # print("app_pid：{}".format(parts[1]))
                    return parts[1]
            return None
        except subprocess.CalledProcessError as e:
            print(f"Error: {e}")
            return None

    # 用pid判断是否崩溃
    def is_app_crash(self, device_identifier, app_package_name):
        app_pid = self.get_app_pid(device_identifier, app_package_name)
        if app_pid is None:
            return 2  # 软件未启动
        elif self.app_pid == app_pid:
            return 0  # 软件正常运行
        return 1  # 软件崩溃

    # 获取资源占用信息
    def get_app_resource_usage(self, device_identifier, app_package_name):
        cpu = 0
        vsize = 0
        rss = 0
        native = 0
        dalvik = 0
        try:
            # 获取 CPU 信息
            cpu_out = os.popen(f"{self.adb} -s {device_identifier} shell dumpsys cpuinfo | findstr {app_package_name}")
            cpu_info = cpu_out.read()
            # print(cpu_info)
            cpu_match = re.search(r'(\d+(?:\.\d+)?)%', cpu_info)
            if cpu_match:
                cpu = cpu_match.group(0)
            # 获取进程信息
            ps_out = os.popen(f"{self.adb} -s {device_identifier} shell ps")
            ps_info = ps_out.read()
            # print(ps_info)
            lines = ps_info.splitlines()
            parts = lines[0].split()
            rss_pos = parts.index("RSS")
            for line in lines:
                parts = line.split()
                if parts[-1] == app_package_name:
                    vsize = parts[rss_pos - 1]
                    rss = parts[rss_pos]
            # 获取内存信息
            meminfo_out = os.popen(f"{self.adb} -s {device_identifier} shell dumpsys meminfo {app_package_name} | findstr Heap")
            meminfo = meminfo_out.read()
            # print(meminfo)
            lines = meminfo.splitlines()
            for line in lines:
                parts = line.split()
                if "Heap" == parts[1]:
                    if "Native" == parts[0]:
                        native = parts[-2]
                    if "Dalvik" == parts[0]:
                        dalvik = parts[-2]
        except Exception as e:
            print(f"Error occurred: {e}")
            with open(self.system_resource_consumption_path, 'a') as f:
                print(f"Error occurred: {e}", file=f)
        # print(cpu, vsize, rss, native, dalvik)
        return cpu, vsize, rss, native, dalvik

    # 启动应用
    def start_app(self, device_identifier, app_package_name, activity_name):
        try:
            command = f"adb -s {device_identifier} shell am start -n {app_package_name}/{activity_name}"
            subprocess.check_call(command, shell=True)
            print(f"App {app_package_name} started successfully.")
        except subprocess.CalledProcessError as e:
            exit_program(f"Error: {e}")

    # 杀死应用
    def stop_app(self, device_identifier, app_package_name):
        try:
            command = f"adb -s {device_identifier} shell am force-stop {app_package_name}"
            subprocess.check_call(command, shell=True)
            print(f"App {app_package_name} stopped successfully.")
        except subprocess.CalledProcessError as e:
            exit_program(f"Error: {e}")

    # 判断应用是否安装
    def is_app_installed(self, device_identifier, app_package_name):
        # 检查输入是否为合法字符串，这里仅做简单的字符检查，可根据需要扩展
        # 检查输入是否为合法字符串，允许包含多个 '.' 但排除其他特殊字符
        if not re.match(r"^[a-zA-Z0-9._-]+([.][a-zA-Z0-9._-]+)*$", app_package_name):
            print("错误：应用程序包名称无效。")
            return False
        try:
            # 执行 dumpsys package 命令来检查应用是否已安装
            command = f"adb -s {device_identifier} shell dumpsys package {app_package_name}"
            result = subprocess.check_output(command, shell=True, universal_newlines=True)

            # 精确匹配应用包名，避免随意输入都认为已安装
            pattern = re.compile(rf"Package \[{app_package_name}\]")
            if pattern.search(result):
                print(f"{app_package_name} 已安装")
                return True
            else:
                print(f"{app_package_name} 未安装")
                return False
        except subprocess.CalledProcessError as e:
            print(f"Error: {e}")
            return False

    # 判断应用是否在运行
    def is_app_running(self, device_identifier, app_package_name):
        try:
            # 执行 adb ps 命令来获取所有进程
            command = f"adb -s {device_identifier} shell ps"
            result = subprocess.check_output(command, shell=True, universal_newlines=True)

            # 检查输出中是否包含应用的包名
            if app_package_name in result:
                print(f"{app_package_name} 已运行")
                return True
            else:
                print(f"{app_package_name} 未运行")
                return False
        except subprocess.CalledProcessError as e:
            print(f"Error: {e}")
            return False




